package com.tianji.learning.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tianji.api.client.course.CatalogueClient;
import com.tianji.api.client.course.CourseClient;
import com.tianji.api.dto.course.CataSimpleInfoDTO;
import com.tianji.api.dto.course.CourseFullInfoDTO;
import com.tianji.api.dto.course.CourseSimpleInfoDTO;
import com.tianji.common.domain.dto.PageDTO;
import com.tianji.common.domain.query.PageQuery;
import com.tianji.common.exceptions.BadRequestException;
import com.tianji.common.utils.CollUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.domain.dto.LearningPlanDTO;
import com.tianji.learning.domain.po.LearningLesson;
import com.tianji.learning.domain.po.LearningRecord;
import com.tianji.learning.domain.vo.LearningLessonVO;
import com.tianji.learning.domain.vo.LearningPlanPageVO;
import com.tianji.learning.domain.vo.LearningPlanVO;
import com.tianji.learning.enums.LessonStatus;
import com.tianji.learning.enums.PlanStatus;
import com.tianji.learning.mapper.LearningLessonMapper;
import com.tianji.learning.mapper.LearningRecordMapper;
import com.tianji.learning.service.ILearningLessonService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.sql.Array;
import java.time.DayOfWeek;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * <p>
 * 学生课程表 服务实现类
 * </p>
 *
 * @author author
 * @since 2025-03-27
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class LearningLessonServiceImpl extends ServiceImpl<LearningLessonMapper, LearningLesson> implements ILearningLessonService {

    private final CourseClient courseClient;

    private final CatalogueClient catalogueClient;
    private final LearningRecordMapper learningRecordMapper;
    @Override
    public void addUserLessons(Long userId, List<Long> courseIds) {
        //1.同步调用feign获取课程信息
        List<CourseSimpleInfoDTO> simpleInfoList = courseClient.getSimpleInfoList(courseIds);
        if (CollUtils.isEmpty(simpleInfoList)) {
            // 课程不存在，无法添加
            log.error("课程信息不存在，无法添加到课表");
            return;
        }
        ArrayList<LearningLesson> list = new ArrayList<>(simpleInfoList.size());
        //2.遍历集合，封装到po中
        for (CourseSimpleInfoDTO simpleInfoDTO : simpleInfoList) {
            //存储用户id，课程id，课程过期时间
            LearningLesson lesson = new LearningLesson();
            lesson.setCourseId(simpleInfoDTO.getId());
            lesson.setUserId(userId);
            //过期时间的单位为月
            LocalDateTime now = LocalDateTime.now();
            //课程过期时间为努力了证明课程是永久的
            if (!(simpleInfoDTO.getValidDuration() ==null)){
                lesson.setExpireTime(now.plusMonths(simpleInfoDTO.getValidDuration()));
            }
            list.add(lesson);
        }
        /* 3.添加到数据库 */
        this.saveBatch(list);
        log.info("用户{}，课程{}，添加到课表成功", userId, courseIds);
    }

    @Override
    public PageDTO<LearningLessonVO> queryMyLessons(PageQuery query) {
        //1.获取当前用户id
        Long userId = UserContext.getUser();
        if (userId == null){
            throw new BadRequestException("请先登录");
        }
        //2.根据当前用户id进行分页查询课程表
        //2.1 page.toMpPage("lastest_learn_time", false))如果前端没有传来排序字段，则默认按照lastest_learn_time进行倒序排序
        Page<LearningLesson> page = this.lambdaQuery()
                .eq(LearningLesson::getUserId, userId)
                .page(query.toMpPage("latest_learn_time", false));
        //3.因为vo对象比po对象多三个参数，所以要用进行同步远程调用查询课程表的简单信息
        //3.1获取总信息
        List<LearningLesson> records = page.getRecords();
        if (CollUtils.isEmpty(records)){
            return PageDTO.empty(page);
        }
        //3.2 通过stream流获取课程id集合
        Set<Long> courseIds = records.stream().map(LearningLesson::getCourseId).collect(Collectors.toSet());
        List<CourseSimpleInfoDTO> simpleInfoList = courseClient.getSimpleInfoList(courseIds);
        if (CollUtils.isEmpty(simpleInfoList)){
            throw new BadRequestException("课程信息不存在");
        }
        //4.封装vo对象
        //4.1通过stream流将simpleInfoList转为map集合map<id,CourseSimpleInfoDTO>
        Map<Long, CourseSimpleInfoDTO> map = simpleInfoList.stream().collect(Collectors.toMap(CourseSimpleInfoDTO::getId, v -> v));
        //4.2遍历records，封装vo对象
        ArrayList<LearningLessonVO> list = new ArrayList<>();
        for (LearningLesson record : records) {
            LearningLessonVO vo = BeanUtil.copyProperties(record, LearningLessonVO.class);
            CourseSimpleInfoDTO courseSimpleInfoDTO = map.get(record.getCourseId());
            if (courseSimpleInfoDTO !=null){
                vo.setCourseName(courseSimpleInfoDTO.getName());
                vo.setCourseCoverUrl(courseSimpleInfoDTO.getCoverUrl());
                vo.setSections(courseSimpleInfoDTO.getSectionNum());
            }
            list.add(vo);
        }
        //5.返回对象
        return PageDTO.of(page, list);
    }

    @Override
    public LearningLessonVO queryNowLearningLesson() {

        //1.获取用户id
        Long userId = UserContext.getUser();
        //2.根据用户id按照最近学习时间进行降序查询我的课程表，且仅查一条数据
        LearningLesson lesson = this.lambdaQuery()
                .eq(LearningLesson::getUserId, userId)
                .orderByDesc(LearningLesson::getLatestLearnTime)
                .last("limit 1")
                .one();
        //2.1做健壮性判断
        if (lesson == null){
           return null;
        }
        //3.使用feign远程调用，为vo的课程名称，封面，总课时赋值
        CourseFullInfoDTO cInfo = courseClient.getCourseInfoById(lesson.getCourseId(), false, false);
        //3.1健壮性判断
        if (cInfo == null){
            throw new BadRequestException("课程信息不存在");
        }
        //4.利用count记录课表中课程总数
        Integer count = this.lambdaQuery().eq(LearningLesson::getUserId, userId).count();
        //5.使用feign远程调用为最近一次学校此课程小节名称以及序号赋值
        List<CataSimpleInfoDTO> infoDTOS = catalogueClient.batchQueryCatalogue(CollUtils.singletonList(lesson.getLatestSectionId()));
        if (CollUtils.isEmpty(infoDTOS)){
            throw new BadRequestException("课程信息不存在");
        }
        //6.封装vo对象并返回
        LearningLessonVO vo = BeanUtil.copyProperties(lesson, LearningLessonVO.class);
        vo.setCourseName(cInfo.getName());
        vo.setCourseCoverUrl(cInfo.getCoverUrl());
        vo.setSections(count);

            CataSimpleInfoDTO infoDTO = infoDTOS.get(0);
            vo.setLatestSectionName(infoDTO.getName());
            vo.setLatestSectionIndex(infoDTO.getCIndex());
        return vo;
    }

    @Override
    public void deleteUserLessons(Long userId, List<Long> courseIds) {
        log.debug("删除用户课程表，用户id：{}，课程id：{}", userId, courseIds);
        courseIds.forEach(courseId -> {
            //1.删除课表
            this.lambdaUpdate()
                    .eq(LearningLesson::getUserId, userId)
                    .eq(LearningLesson::getCourseId, courseId)
                    .remove();
        });
    }

    @Override
    public Integer isLessonValid(Long courseId) {
        LearningLesson lesson = this.lambdaQuery()
                .eq(LearningLesson::getCourseId, courseId)
                .eq(LearningLesson::getStatus, LessonStatus.LEARNING)
                .one();
        if (lesson == null || lesson.getId()==null){
            return null;
        }
        return Math.toIntExact(lesson.getId());
    }

    @Override
    public LearningLessonVO queryLessonStatus(Long courseId) {
        //1.获取当前用户id
        Long userId = UserContext.getUser();
        LearningLesson lesson = lambdaQuery().eq(LearningLesson::getUserId, userId).eq(LearningLesson::getCourseId, courseId).one();
        if (lesson == null){
            return null;
        }
        LearningLessonVO vo = new LearningLessonVO();
        vo.setId(lesson.getId());
        vo.setCourseId(lesson.getCourseId());
        vo.setStatus(lesson.getStatus());
        vo.setPlanStatus(lesson.getPlanStatus());
        vo.setCreateTime(lesson.getCreateTime());
        if (lesson.getExpireTime() != null){
            vo.setExpireTime(lesson.getExpireTime());
        }
        return vo;
    }

    @Override
    public Integer countLearningLessonByCourse(Long courseId) {
        //select count(distinct user_id) from learning_lesson where course_id = ?
        //Integer count=  baseMapper.countByCourseId(courseId);
        Integer count = lambdaQuery()
                .eq(LearningLesson::getCourseId, courseId)
                .count();
        if (count == null){
            count = 0;
        }
        return count;
    }

    @Override
    public void createStudyPlans(LearningPlanDTO learningPlanDTO) {
        //1.获取当前登录用户id
        Long userId = UserContext.getUser();
        //2.创建学习计划
        boolean update = this.lambdaUpdate()
                .set(LearningLesson::getWeekFreq, learningPlanDTO.getFreq())
                .eq(LearningLesson::getUserId, userId)
                .eq(LearningLesson::getCourseId, learningPlanDTO.getCourseId())
                .update();
        if (!update){
            throw new BadRequestException("创建学习计划失败");
        }
    }

    @Override
    public LearningPlanPageVO queryMyLearningPlans(PageQuery query) {
        //1.获取当前登录用户id
        Long userId = UserContext.getUser();
        //2.TODO 查询积分奖励
        //3.获取当前时间，并计算这周的周一和周日
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime monday = now.with(DayOfWeek.MONDAY).withHour(0).withMinute(0).withSecond(0);
        LocalDateTime sunday = now.with(DayOfWeek.SUNDAY).withHour(23).withMinute(59).withSecond(59);
        //4.查询课表获取本周的学习计划数量总和
        QueryWrapper<LearningLesson> wrapper = new QueryWrapper<>();
        wrapper.select("sum(week_freq) as planTotalSum");
        wrapper.eq("user_id", userId);
        wrapper.eq("plan_status", PlanStatus.PLAN_RUNNING);
        wrapper.in("status", LessonStatus.LEARNING, LessonStatus.NOT_BEGIN);
        Map<String, Object> map = this.getMap(wrapper);
        if (map == null || map.get("planTotalSum")==null){
            return null;
        }
        Integer planTotalSum = Integer.valueOf(map.get("planTotalSum").toString());
        //5.查询本周实际的学习计划数量总和 select count(*) from learning_record where finished = 1 and user_id = ? and update_time between ? and ?
        Integer count = learningRecordMapper.selectCount(new QueryWrapper<LearningRecord>()
                .eq("user_id", userId)
                .eq("finished", true)
                .between("update_time", monday, sunday)
        );
        //6.查询lesson表
        Page<LearningLesson> lessonPage = this.lambdaQuery().eq(LearningLesson::getUserId, userId)
                .eq(LearningLesson::getPlanStatus, PlanStatus.PLAN_RUNNING)
                .in(LearningLesson::getStatus, LessonStatus.LEARNING, LessonStatus.NOT_BEGIN)
                .page(query.toMpPage("create_time", false));
        if (CollUtils.isEmpty(lessonPage.getRecords())){
            throw new BadRequestException("本周没有学习计划");
        }
        List<LearningLesson> records = lessonPage.getRecords();
        //使用stream流获取课程id
        List<Long> courseIds = records.stream().map(LearningLesson::getCourseId).collect(Collectors.toList());
        //通过远程调用查询课程信息
        List<CourseSimpleInfoDTO> cInfoList = courseClient.getSimpleInfoList(courseIds);
        if (CollUtils.isEmpty(cInfoList)){
            throw new BadRequestException("课程信息不存在");
        }
        //利用stream流将cInfoList转化为map<courseId,CourseSimpleInfoDTO>
        Map<Long, CourseSimpleInfoDTO> cinfoMap = cInfoList.stream().collect(Collectors.toMap(CourseSimpleInfoDTO::getId, cInfo -> cInfo));
        LearningPlanPageVO vo = new LearningPlanPageVO();
        ArrayList<LearningPlanVO> list = new ArrayList<>();
        for (LearningLesson record : records) {
            LearningPlanVO planVO = new LearningPlanVO();
            //1.获取课程名称以及课程总小节数量
            String courseName = cinfoMap.get(record.getCourseId()).getName();
            Integer sectionNum = cinfoMap.get(record.getCourseId()).getSectionNum();
            //本周该课程的计划学习数量
            Integer weekFreq = record.getWeekFreq();
            //根据lesson_id查询学习记录表
            Integer aStudyNum = learningRecordMapper.selectCount(new QueryWrapper<LearningRecord>()
                    .eq("lesson_id", record.getId())
                    .eq("finished", true)
                    .between("finish_time", monday, sunday)
            );
            planVO.setCourseId(record.getCourseId());//课程id
            planVO.setCourseName(courseName);//课程名称
            planVO.setWeekFreq(weekFreq);//每周计划学习数量
            planVO.setSections(sectionNum);//课程总小节数量
            planVO.setWeekLearnedSections(aStudyNum);//本周已学习数量
            planVO.setLearnedSections(record.getLearnedSections());//该课程总已学习数量
            planVO.setLatestLearnTime(record.getLatestLearnTime());//最近一次学习时间
            list.add(planVO);
        }
        vo.setList(list);
        vo.setWeekTotalPlan(planTotalSum);//本周计划学习总数
        vo.setWeekFinished(count);//本周已学习总数
        vo.setWeekPoints(0);
        return vo;
    }
}

































